﻿using Dx.DDD.Domain;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System.Threading;

namespace Dx.DDD.Repositories
{
    public class UnitOfWorkMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<UnitOfWorkMiddleware> _logger;
        public UnitOfWorkMiddleware(
            ILogger<UnitOfWorkMiddleware> logger,
            RequestDelegate next
            )
        {
            _logger = logger;
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {


            CancellationToken cancellationToken = context.RequestAborted;
            var endpoint = context.GetEndpoint();
            var controllerActionDescriptor = endpoint?.Metadata?.GetMetadata<ControllerActionDescriptor>();
            var fullName = controllerActionDescriptor?.DisplayName?.SubstringBeforeSpace();

            if (string.IsNullOrWhiteSpace(fullName) || !fullName.IsUnitOfWork(out var unitOfWorks))
            {
                await _next(context);
                return;
            }
            var sdc = context.RequestServices.GetServices<DbContext>();
            //TODO:也可以考虑在此处保存SaveChangesAsync
            //var transactions = await GetTransactionList(unitOfWorks, cancellationToken);
            List<IDbContextTransaction> transactions = await GetTransactionList(sdc, unitOfWorks, cancellationToken);
            try
            {
                await _next(context);
                foreach (var transaction in transactions)
                {
                    await transaction.CommitAsync(cancellationToken);
                }

            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "工作单元错误");
                foreach (var transaction in transactions)
                {
                    try
                    {
                        await transaction.RollbackAsync(cancellationToken);
                    }
                    catch (Exception ex1)
                    {
                        _logger.LogError($"Failed to commit transaction: {ex1}");
                        continue;
                    }
                }
            }
            finally
            {
                foreach (var transaction in transactions)
                {
                    try
                    {
                        await transaction.DisposeAsync();
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "事物关闭");
                        continue;
                    }

                }
            }
        }

        private async Task<List<IDbContextTransaction>> GetTransactionList(IEnumerable<DbContext> sdc, HashSet<string> unitOfWorks, CancellationToken cancellation)
        {
            var dbs = sdc.Where(x => unitOfWorks.Contains(x.GetType().Name));
            var list = new List<IDbContextTransaction>();
            foreach (var db in dbs)
            {
                IDbContextTransaction? transaction = await db.Database.BeginTransactionAsync(cancellation);
                list.Add(transaction);
            }
            return list;
        }

        //private async Task<List<IDbContextTransaction>> GetTransactionList(HashSet<UnitOfWorkAttribute> unitOfWorks,CancellationToken cancellation)
        //{
        //    var list = new List<IDbContextTransaction>();
        //    foreach (var unitOfWork in unitOfWorks) 
        //    {
        //        var db = _dbContextFactory.GetDbContext(unitOfWork.ConnectionString);
        //         IDbContextTransaction? transaction = await db.Database.BeginTransactionAsync(cancellation);
        //        list.Add(transaction);
        //    }
        //    return list;
        //}
    }
}
